﻿using System;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows;
using System.Windows.Media.Animation;
using System.Diagnostics;
using System.Windows.Input;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Windows.Data;
using System.Windows.Media;

namespace UBCon.Navigation
{
    using UBCon.Core.Utilities;
    using UBCon.Navigation.Models;
    using UBCon.Core.Commands;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.IO;
    using System.Reflection;
    using System.Windows.Documents;
    using UBCon.Core.Enums;
    //using System.Windows.Shapes;

    /// <summary>
    /// Contains list of selectable items.
    /// </summary>
    [TemplatePart(Name = MenuName, Type = typeof(DropDownMenu))]
    public class OutlookBar : ListBox
    {
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
        #region Constructors

        /// <summary>
        /// Default static constructor.
        /// </summary>
        static OutlookBar()
        {
            Type ownerType = typeof(OutlookBar);

            OutlookBar.DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(ownerType));

            OutlookBar.SelectionModeProperty.OverrideMetadata(ownerType,
                new FrameworkPropertyMetadata(SelectionMode.Single, new PropertyChangedCallback(OnSelectionModePropertyChanged),
                    new CoerceValueCallback(CoerceSelectionModeProperty)));
        }

        /// <summary>
        /// Initializes a new instance of the UBCon.Navigation.OutlookBar class.
        /// </summary>
        public OutlookBar()
            : base()
        {
            CollapsExpandCommand = new Core.Commands.Command(OnCollapsExpandCommandExecuted);
            ExpandWidth = PaneWidth;

            //on load we will update items.
            Loaded += OnOutlookBarLoaded;
            MenuItems = new ObservableCollection<DropDownMenuItem>();
            MenuItemCommand = new Command(OnMenuItemSelected);
            ShowNavigationSettingsCommand = new Command(OnShowNavigationSettingsCommandExecuted);
            BuildNavigationSettingsMenuItem();
            RenderTransform = new TranslateTransform();
            upDownCommand = new Command(OnUpDownCommandExecuted);

        }

        /// <summary>
        /// /// Invoked whenever show navigation settings button is pressed.
        /// </summary>
        private void OnShowNavigationSettingsCommandExecuted()
        {
            if (NavigationSettingsWindowContent != null)
            {
                NavigationSettingsWindow = new XWindow()
                {
                    IsMaximizeButtonEnabled = false,
                    IsMinimizeButtonEnabled = false,
                    Width = 320,
                    Height = 275,
                    Content = NavigationSettingsWindowContent,
                    WindowStartupLocation = WindowStartupLocation.CenterScreen,
                    ResizeMode = ResizeMode.NoResize
                };
                GetNumericUpDown();
                if (numericUpDown != null)
                {
                    numericUpDown.MinValue = Items.Count > 0 ? 1 : 0;
                    numericUpDown.MaxValue = Items.Count;
                    numericUpDown.Value = MaxVisibleItemsCount;
                }
                if (checkBox != null)
                {
                    checkBox.IsChecked = IsCompact;
                }
                if (okButton != null)
                {
                    okButton.Click += okButton_Click;
                }
                if (listBox != null)
                {
                    models.Clear();

                    foreach (var item in Items)
                    {
                        if (item is OutlookBarItem)
                        {
                            OutlookBarItem barItem = item as OutlookBarItem;
                            models.Add(new OutlookBarItemModel() { Title = barItem.Title, OutlookBarItem = barItem });
                        }
                    }
                    listBox.ItemsSource = models;
                    listBox.SelectionChanged += listBox_SelectionChanged;
                    listBox.SelectedIndex = SelectedIndex;
                    upDownCommand.CanExecute = SelectedIndex >= 0;

                }
                if (upButton != null)
                {
                    upButton.Command = upDownCommand;
                    upButton.CommandParameter = "Up";
                }
                if (downButton != null)
                {
                    downButton.Command = upDownCommand;
                    downButton.CommandParameter = "Down";
                }


                NavigationSettingsWindow.Closing += NavigationSettingsWindow_Closing;
                NavigationSettingsWindow.ShowDialog();
            }
        }

        /// <summary>
        /// Invoked whenever collapsed or expanded state is requested.
        /// </summary>
        /// <param name="parameter">if value is "Collapse" then OutlookBar's pane is collapsed, "Expand" otherwise.</param>
        private void OnCollapsExpandCommandExecuted(object parameter)
        {
            if (parameter == null)
            {
                return;
            }

            string command = (string)parameter;

            if (!string.IsNullOrEmpty(command))
            {
                if (command == "Collapse")
                {
                    if (CollapseStoryboard != null)
                    {
                        storedCount = hVisibleItems;

                        if (AnimationsEnabled && pHelper.CanAnimate)
                        {
                            CollapseStoryboard.Begin();
                        }
                        else
                        {
                            ExpandWidth = PaneWidth;
                            PaneWidth = 50.0D;
                            IsMinimized = true;
                        }
                    }
                }
                else if (command == "Expand")
                {
                    if (ExpandStoryboard != null && expandDA != null)
                    {
                        if (NavigationItemsHost != null)
                        {
                            NavigationItemsHost.Opacity = 0.0;
                        }
                        if (IsCompact)
                        {
                            CollapseAllItems();
                        }
                        if (AnimationsEnabled && pHelper.CanAnimate)
                        {
                            if (storedCount == 0 && IsCompact)
                            {
                                ExpandWidth = storeMinWidth;
                            }

                            expandDA.To = ExpandWidth;

                            ExpandStoryboard.Begin();
                        }
                        else
                        {
                            IsMinimized = false;
                            PaneWidth = ExpandWidth;
                            OnOutlookBarExpanded();
                        }
                    }
                }
            }
        }

        #endregion

        //------------------------------------------------------
        //
        //  Properties
        //
        //------------------------------------------------------
        #region Properties

        /// <summary>
        /// Gets or sets a value that indicates whether the content is 
        /// stationary when the user drags the Thumb of a OulookBar. This a dependency property.
        /// </summary>
        public bool IsDeferredDrag
        {
            get { return (bool)GetValue(IsDeferredDragProperty); }
            set { SetValue(IsDeferredDragProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.IsDeferredDrag dependency property.
        /// </summary>
        public static readonly DependencyProperty IsDeferredDragProperty =
            DependencyProperty.Register("IsDeferredDrag", typeof(bool), typeof(OutlookBar), new PropertyMetadata(false));

        /// <summary>
        /// Gets or sets a boolean value which indicates whether OutlookBar control can use advanced animations.
        /// Note: Before enabling advanced animations hardware's RenderCapability.Tier property is also considered.
        /// This is a dependency property.
        /// </summary>
        [Bindable(true),
         ReadOnly(false),
         Category("Behaviour")]
        public bool AnimationsEnabled
        {
            get { return (bool)GetValue(AnimationsEnabledProperty); }
            set { SetValue(AnimationsEnabledProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.AnimationsEnabled dependency property.
        /// </summary>
        public static readonly DependencyProperty AnimationsEnabledProperty =
            DependencyProperty.Register("AnimationsEnabled", typeof(bool), typeof(OutlookBar),
            new FrameworkPropertyMetadata(true,
                FrameworkPropertyMetadataOptions.Journal | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                new PropertyChangedCallback(OnAnimationsEnabledChanged)));

        /// <summary>
        /// Gets or sets a boolean value indicating whether control's last modified state must be saved or not.
        /// This is a dependency property.
        /// </summary>
        public bool SavesState
        {
            get { return (bool)GetValue(SavesStateProperty); }
            set { SetValue(SavesStateProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.SavesState dependency property.
        /// </summary>
        public static readonly DependencyProperty SavesStateProperty =
            DependencyProperty.Register("SavesState", typeof(bool), typeof(OutlookBar),
            new FrameworkPropertyMetadata(true));

        /// <summary>
        /// Gets or sets visible items count when control is not in minimized state.
        /// </summary>
        private int hVisibleItems
        {
            get
            {
                return _hVisibleItems;
            }
            set
            {
                _hVisibleItems = value;

                SetValue(HasHorizontallHiddenItemsProperty, ActualItemsCount - value > 0);
            }
        }

        /// <summary>
        /// Gets or sets visible items count when control is in minimized state.
        /// </summary>
        private int vVisibleItems
        {
            get
            {
                return _vVisibleItems;
            }
            set
            {
                _vVisibleItems = value;

                SetValue(HasVerticalHiddenItemsProperty, ActualItemsCount - value > 0);
            }
        }

        /// <summary>
        /// Storyboard to animate transition when selection changes.
        /// </summary>
        internal Storyboard TranslateStoryboard
        {
            get
            {
                return translateStoryboard;
            }
            set
            {
                translateStoryboard = value;
            }
        }

        /// <summary>
        /// Builds transition animation when selection changes.
        /// </summary>
        private void BuildTranslateAnimation()
        {
            TranslateStoryboard = new Storyboard();

            translateDA = new DoubleAnimation() { EasingFunction = new ExponentialEase() { EasingMode = EasingMode.EaseOut, Exponent = 5 } };

            translateDA.Duration = new Duration(TimeSpan.FromSeconds(0.25));

            Storyboard.SetTarget(translateDA, this);

            Storyboard.SetTargetProperty(translateDA, new PropertyPath(xPropertyPath));

            TranslateStoryboard.Children.Add(translateDA);

            opacityDA = new DoubleAnimation();

            opacityDA.Duration = new Duration(TimeSpan.FromSeconds(0.10));

            Storyboard.SetTarget(opacityDA, this);

            Storyboard.SetTargetProperty(opacityDA, new PropertyPath("Opacity"));

            TranslateStoryboard.Children.Add(opacityDA);
        }

        /// <summary>
        /// Gets or sets a boolean value which indicates whether OutlookBar control is compact or not.
        /// This is a dependency property.
        /// </summary>
        public bool IsCompact
        {
            get { return (bool)GetValue(IsCompactProperty); }
            set { SetValue(IsCompactProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.IsCompact dependency property.
        /// </summary>
        public static readonly DependencyProperty IsCompactProperty =
            DependencyProperty.Register("IsCompact", typeof(bool), typeof(OutlookBar), new
                FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsMeasure,
                new PropertyChangedCallback(OutlookBar.OnIsCompactPropertyChanged)));

        /// <summary>
        /// Gets or sets inner content of OutlookBar.
        /// </summary>
        public object InnerContent
        {
            get { return (object)GetValue(InnerContentProperty); }
            set { SetValue(InnerContentProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.InnerContent dependency property.
        /// </summary>
        public static readonly DependencyProperty InnerContentProperty =
            DependencyProperty.Register("InnerContent", typeof(object), typeof(OutlookBar), new FrameworkPropertyMetadata(null));

        /// <summary>
        /// Get or sets width of outlook bar.
        /// This is a dependency property.
        /// </summary>
        public double PaneWidth
        {
            get { return (double)GetValue(PaneWidthProperty); }
            set { SetValue(PaneWidthProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.PaneWidth dependency property.
        /// </summary>
        public static readonly DependencyProperty PaneWidthProperty =
            DependencyProperty.Register("PaneWidth", typeof(double), typeof(OutlookBar),
            new FrameworkPropertyMetadata(300.0D,
                new PropertyChangedCallback(OnPaneWidthPropertyChanged),
                new CoerceValueCallback(CoercePaneWidthProperty)));

        /// <summary>
        /// Gets or sets PaneWidth's value. This is a dependency property. 
        /// </summary>
        internal double ItemsPaneWidth
        {
            get { return (double)GetValue(ItemsPaneWidthProperty); }
            set { SetValue(ItemsPaneWidthProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.ItemsPaneWidth dependency property.
        /// </summary>
        internal static readonly DependencyProperty ItemsPaneWidthProperty =
            DependencyProperty.Register("ItemsPaneWidth", typeof(double), typeof(OutlookBar),
            new FrameworkPropertyMetadata(297.0D));

        /// <summary>
        /// Gets or sets a boolean value which indicates OutlookBar is minimized or not.
        /// This is a dependency property.
        /// </summary>
        public bool IsMinimized
        {
            get { return (bool)GetValue(IsMinimizedProperty); }
            set { SetValue(IsMinimizedProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.IsMinimized dependency property.
        /// </summary>
        public static readonly DependencyProperty IsMinimizedProperty =
            DependencyProperty.Register("IsMinimized", typeof(bool), typeof(OutlookBar),
            new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsMinimizedPropertyChanged)));

        /// <summary>
        /// Gets or sets maximum width of OutlookBar's left pane. 
        /// This is a dependency property.
        /// </summary>
        public double MaxPaneWidth
        {
            get { return (double)GetValue(MaxPaneWidthProperty); }
            set { SetValue(MaxPaneWidthProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.MaxPaneWidth dependency property.
        /// </summary>
        public static readonly DependencyProperty MaxPaneWidthProperty =
            DependencyProperty.Register("MaxPaneWidth", typeof(double), typeof(OutlookBar),
            new FrameworkPropertyMetadata(600.0D));

        /// <summary>
        /// Gets or sets a command which is executed when collapsed or expanded state is requested.
        /// This is a dependency property.
        /// </summary>
        internal Command CollapsExpandCommand
        {
            get { return (UBCon.Core.Commands.Command)GetValue(CollapsExpandCommandProperty); }
            set { SetValue(CollapsExpandCommandProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.CollapsExpandCommand dependency property.
        /// </summary>
        internal static readonly DependencyProperty CollapsExpandCommandProperty =
            DependencyProperty.Register("CollapsExpandCommand", typeof(UBCon.Core.Commands.Command), typeof(OutlookBar));

        /// <summary>
        /// Gets or sets maximum visible items count. This is dependency property.
        /// </summary>
        [Bindable(true),
        ReadOnly(false),
        Browsable(true)]
        public int MaxVisibleItemsCount
        {
            get { return (int)GetValue(MaxVisibleItemsCountProperty); }
            set { SetValue(MaxVisibleItemsCountProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.MaxVisibleItemsCount dependency property.
        /// </summary>
        public static readonly DependencyProperty MaxVisibleItemsCountProperty =
            DependencyProperty.Register("MaxVisibleItemsCount", typeof(int), typeof(OutlookBar),
            new FrameworkPropertyMetadata(5, FrameworkPropertyMetadataOptions.AffectsMeasure,
                new PropertyChangedCallback(OnMaxVisibleItemsCountPropertyChanged),
                new CoerceValueCallback(CoerceMaxVisibleItemsCountProperty)));

        /// <summary>
        /// Gets or sets Grid's height value which is changed when on drag and OutlookBar is in minimized state.
        /// This is a dependency property.
        /// </summary>
        internal double ItemsGridHeight
        {
            get { return (double)GetValue(ItemsGridHeightProperty); }
            set { SetValue(ItemsGridHeightProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.ItemsGridHeight attached property.
        /// </summary>
        internal static readonly DependencyProperty ItemsGridHeightProperty =
            DependencyProperty.Register("ItemsGridHeight", typeof(double), typeof(OutlookBar),
            new FrameworkPropertyMetadata(double.NaN,
                new PropertyChangedCallback(OnItemsGridHeightChanged),
                new CoerceValueCallback(CoerceItemsGridHeight)));

        internal static DropDownMenuItem GetDropDownMenuItem(DependencyObject obj)
        {
            return (DropDownMenuItem)obj.GetValue(DropDownMenuItemProperty);
        }

        internal static void SetDropDownMenuItem(DependencyObject obj, DropDownMenuItem value)
        {
            obj.SetValue(DropDownMenuItemProperty, value);
        }


        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.DropDownMenuItem dependency property.
        /// </summary>
        internal static readonly DependencyProperty DropDownMenuItemProperty =
            DependencyProperty.RegisterAttached("DropDownMenuItem", typeof(DropDownMenuItem), typeof(OutlookBar), 
            new FrameworkPropertyMetadata(null));

        /// <summary>
        /// Gets or sets collection of DropDownMenuItem control.
        /// </summary>
        internal ObservableCollection<DropDownMenuItem> MenuItems
        {
            get { return (ObservableCollection<DropDownMenuItem>)GetValue(MenuItemsProperty); }
            set { SetValue(MenuItemsProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.MenuItems dependency property.
        /// </summary>
        internal static readonly DependencyProperty MenuItemsProperty =
            DependencyProperty.Register("MenuItems", typeof(ObservableCollection<DropDownMenuItem>), typeof(OutlookBar),
            new FrameworkPropertyMetadata(null));

        /// <summary>
        /// Invoked whenever related DropDownMenuItem is pressed.
        /// </summary>
        internal Command MenuItemCommand
        {
            get
            {
                return menuItemCommand;
            }
            set
            {
                menuItemCommand = value;
            }
        }

        /// <summary>
        /// Invoked whenever the button, that shows navigation settings window, is pressed.
        /// </summary>
        internal Command ShowNavigationSettingsCommand
        {
            get
            {
                return showNavigationSettingsCommand;
            }
            set
            {
                showNavigationSettingsCommand = value;
            }
        }

        /// <summary>
        /// Gets or sets Storyboard animation that minimizes OutlookBar.
        /// </summary>
        internal Storyboard CollapseStoryboard
        {
            get
            {
                if (collapseStoryboard == null)
                {
                    BuildCollapsExpandAnimations(false);
                }
                return collapseStoryboard;
            }
            set
            {
                //Before setting a new value we must check whether the new and old values are null or not.
                //If the new one is null while the old is not we must detach event handler from old value
                //for perfimance issue
                if (value == null && collapseStoryboard != null)
                {
                    CollapseStoryboard.Completed -= CollapseStoryboard_Completed;
                }
                collapseStoryboard = value;
            }
        }

        /// <summary>
        /// Gets or sets Storyboard animation that expands OutlookBar.
        /// </summary>
        internal Storyboard ExpandStoryboard
        {
            get
            {
                if (expandStoryboard == null)
                {
                    BuildCollapsExpandAnimations();
                }
                return expandStoryboard;
            }
            set
            {
                //Before setting a new value we must check whether the new and old values are null or not.
                //If the new one is null while the old is not we must detach event handler from old value
                //for perfimance issue
                if (value == null && expandStoryboard != null)
                {
                    ExpandStoryboard.Completed -= ExpandStoryboard_Completed;
                }
                expandStoryboard = value;
            }
        }

        /// <summary>
        /// Gets or sets instance of XWindow in which navigation settings content is held.
        /// </summary>
        private XWindow NavigationSettingsWindow
        {
            get;
            set;
        }

        /// <summary>
        /// Gets a boolean value which indicates control has collapsed items when it is not in minimized state.
        /// This is a dependency property.
        /// </summary>
        internal bool HasHorizontallHiddenItems
        {
            get { return (bool)GetValue(HasHorizontallHiddenItemsProperty); }
            set { SetValue(HasHorizontallHiddenItemsProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.HasHorizontallHiddenItems dependency property.
        /// </summary>
        internal static readonly DependencyProperty HasHorizontallHiddenItemsProperty =
            DependencyProperty.Register("HasHorizontallHiddenItems", typeof(bool), typeof(OutlookBar), new PropertyMetadata(false));

        /// <summary>
        ///  Gets a boolean value which indicates control has collapsed items when it is in minimized state.
        /// This is a dependency property.
        /// </summary>
        internal bool HasVerticalHiddenItems
        {
            get { return (bool)GetValue(HasVerticalHiddenItemsProperty); }
            set { SetValue(HasVerticalHiddenItemsProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.HasVerticalHiddenItems dependency property.
        /// </summary>
        internal static readonly DependencyProperty HasVerticalHiddenItemsProperty =
            DependencyProperty.Register("HasVerticalHiddenItems", typeof(bool), typeof(OutlookBar), new PropertyMetadata(false));

        /// <summary>
        /// Gets os sets a boolean value which indicates whether transition animation is enabled when selection changes. 
        /// This is a dependency property.
        /// </summary>
        [Bindable(true),
        ReadOnly(false),
        Category("Behaviour")]
        public bool AnimatedSelection
        {
            get { return (bool)GetValue(AnimatedSelectionProperty); }
            set { SetValue(AnimatedSelectionProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.AnimatedSelection dependency property.
        /// </summary>
        public static readonly DependencyProperty AnimatedSelectionProperty =
            DependencyProperty.Register("AnimatedSelection", typeof(bool), typeof(OutlookBar),
            new PropertyMetadata(false));

        /// <summary>
        /// Gets a boolean value indicating whether PaneWidth property has a valid value.
        /// </summary>
        internal bool IsValidPaneWidth
        {
            get { return PaneWidth >= 50 && PaneWidth <= MaxPaneWidth; }
        }

        /// <summary>
        /// Gets or sets control's style.
        /// This is a dependency property.
        /// </summary>
        public OutlookBarStyles OutlookBarStyle
        {
            get { return (OutlookBarStyles)GetValue(OutlookBarStyleProperty); }
            set { SetValue(OutlookBarStyleProperty, value); }
        }

        /// <summary>
        /// Identifies the UBCon.Navigation.OutlookBar.OutlookBarStyle dependency property.
        /// </summary>
        public static readonly DependencyProperty OutlookBarStyleProperty =
            DependencyProperty.Register("OutlookBarStyle", typeof(OutlookBarStyles), typeof(OutlookBar), 
            new PropertyMetadata(OutlookBarStyles.Outlook2013));
        
        #endregion

        //------------------------------------------------------
        //
        //  Event Handlers
        //
        //------------------------------------------------------
        #region Event Handlers

        /// <summary>
        /// Occurs when ListBox control raises SelectionChange event.
        /// </summary>
        /// <param name="sender">Owner of the event.</param>
        /// <param name="e">Event data for the event.</param>
        private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            upDownCommand.CanExecute = listBox.SelectedIndex >= 0 ? true : false;
        }

        /// <summary>
        /// Occurs directly after Close is called, and can be handled to cancel window closure.
        /// </summary>
        /// <param name="sender">Owner of the event.</param>
        /// <param name="e">Event data for the event.</param>
        private void NavigationSettingsWindow_Closing(object sender, CancelEventArgs e)
        {
            if (okButton != null)
            {
                okButton.Click -= okButton_Click;
            }
            if (listBox != null)
            {
                listBox.SelectionChanged -= listBox_SelectionChanged;
            }
            upDownCommand.CanExecute = false;
        }

        /// <summary>
        /// Invoked when submit button is pressed.
        /// </summary>
        /// <param name="sender">Owner of the event.</param>
        /// <param name="e">Event data for the event.</param>
        private void okButton_Click(object sender, RoutedEventArgs e)
        {
            e.Handled = true;

            if (numericUpDown != null)
            {
                MaxVisibleItemsCount = numericUpDown.Value;
            }
            if (checkBox != null)
            {
                IsCompact = (bool)checkBox.IsChecked;
            }

            NavigationSettingsWindow.Close();



            if (swapPanel != null)
            {
                //swapPanel.EnsureItemsLocatedCorrectly(indexes);
                swapPanel.InvalidateArrange();
            }
        }

        /// <summary>
        /// Invoked whenever Thumb control raises its DragDeltaEvent event.
        /// </summary>
        /// <param name="sender">Owner of the event.</param>
        /// <param name="e">Event data for the event.</param>
        private void upDownThumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {
            e.Handled = true;

            //if (double.IsNaN(ItemsGridHeight))
            //{
            //    //  ItemsGridHeight = 0;
            //}

            double vMove = Mouse.GetPosition(this).Y;

            //double deltaV = Math.Min(-e.VerticalChange,
            //            itemsGrid.ActualHeight - itemsGrid.MinHeight);
            //ItemsGridHeight += deltaV;
            if (vOffset < vMove && vMove - vOffset >= avItemH && vVisibleItems > 0 && vVisibleItems <= ActualItemsCount)
            {
                ////vVerticalItem = ValidateIndex(vVerticalItem);
                //if (vVerticalItem <= 0 || vVerticalItem > ActualItemsCount)
                //{
                //    vVerticalItem = ActualItemsCount;
                //}

                SwapPanelItem model = (from m in swapPanel.ItemsModel where m.Index == vVisibleItems - 1 select m).FirstOrDefault();

                OutlookBarItem item = null;

                if (model != null)
                {
                    item = model.UIElement as OutlookBarItem;
                }

                if (item != null && item.Visibility == System.Windows.Visibility.Visible)
                {
                    double vheight = item.DesiredSize.Height;

                    item.Visibility = System.Windows.Visibility.Collapsed;

                    OutlookBarItemModel itemModel = item._itemModel;

                    if (itemModel == null)
                    {
                        itemModel = new OutlookBarItemModel()
                        {
                            Title = item.Title,
                            OutlookBarItem = item,
                            Size = item.DesiredSize
                        };
                        item._itemModel = itemModel;
                    }

                    vOffset += vheight;

                    ItemsGridHeight = ItemsGridHeight - vheight - 3;

                    vVisibleItems--;
                }
                else
                {
                    upDownThumb.CancelDrag();
                }

            }
            else if (vOffset > vMove && vOffset - vMove >= avItemH && vVisibleItems < ActualItemsCount)
            {

                SwapPanelItem model = (from m in swapPanel.ItemsModel where m.Index == vVisibleItems select m).FirstOrDefault();

                OutlookBarItem item = null;

                if (model != null)
                {
                    item = model.UIElement as OutlookBarItem;
                }

                if (item != null && item.Visibility == System.Windows.Visibility.Collapsed)
                {

                    item.Visibility = System.Windows.Visibility.Visible;

                    if (!item.IsMeasureValid)
                    {
                        item.Measure(infiniteSize);
                    }

                    double vheight = item.DesiredSize.Height;

                    vOffset -= vheight;

                    ItemsGridHeight = ItemsGridHeight + vheight + 3;

                    vVisibleItems++;
                }
                else
                {
                    upDownThumb.CancelDrag();
                }
            }

        }

        double vOffset = 0;

        /// <summary>
        /// Invoked whenever Thumb control raises its DragDeltaStartedEvent event.
        /// </summary>
        /// <param name="sender">Owner of the event.</param>
        /// <param name="e">Event data for the event.</param>
        private void upDownThumb_DragStarted(object sender, System.Windows.Controls.Primitives.DragStartedEventArgs e)
        {
            vOffset = Mouse.GetPosition(this).Y;

        }

        /// <summary>
        /// Invoked whenever Thumb control raises its DragDeltaEvent event.
        /// </summary>
        /// <param name="sender">Owner of the event.</param>
        /// <param name="e">Event data for the event.</param>
        private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {
            e.Handled = true;

            if (IsDeferredDrag && _deferredDragAdorner != null)
            {
                double pendingWidth = PaneWidth + e.HorizontalChange;
                if (pendingWidth > 50 && pendingWidth < MaxPaneWidth)
                {
                    _deferredDragAdorner.UpdatePosition(e.HorizontalChange);
                }
            }
            else
            {
                PaneWidth += e.HorizontalChange;

                if (IsMinimized && e.HorizontalChange <= 0 ||
                MS.Internal.DoubleUtil.IsZero(MaxPaneWidth - PaneWidth) && e.HorizontalChange >= 0)
                {
                    (sender as Thumb).CancelDrag();
                }
            }
        }

        /// <summary>
        /// Occurs when collapsing has completely finished playing: it will no longer enter its active period. 
        /// (Inherited from Timeline.)
        /// </summary>
        /// <param name="sender">Owner of the event.</param>
        /// <param name="e">Event data for the event.</param>
        private void CollapseStoryboard_Completed(object sender, EventArgs e)
        {
            IsMinimized = true;

            //This trick enables us to get rid off dependency on AnimationTimeline.
            //If the value of dependency property is modified by animation
            //we cannot set value explicitly.
            PaneWidth = PaneWidth;

            ApplyAnimationClock(OutlookBar.PaneWidthProperty, null);

            //CollapseAllItems();

            //MakeVisibleCollapsedItems(verticalDrag_visibleItems);
        }

        /// <summary>
        /// Occurs when expanding has completely finished playing: it will no longer enter its active period. 
        /// (Inherited from Timeline.)
        /// </summary>
        /// <param name="sender">Owner of the event.</param>
        /// <param name="e">Event data for the event.</param>
        private void ExpandStoryboard_Completed(object sender, EventArgs e)
        {
            IsMinimized = false;

            //This trick enables us to get rid off dependency on AnimationTimeline.
            //If the value of dependency property is modified by animation
            //we cannot set value explicitly.
            PaneWidth = PaneWidth;

            ApplyAnimationClock(OutlookBar.PaneWidthProperty, null);

            OnOutlookBarExpanded();

        }

        /// <summary>
        /// Performs needed actions when OutlookBar is expanded.
        /// </summary>
        private void OnOutlookBarExpanded()
        {
            if (IsCompact)
            {
                hVisibleItems = storedCount != 0 ? storedCount : 3;

                MakeVisibleItems(hVisibleItems);
            }

            if (NavigationItemsHost != null)
            {
                NavigationItemsHost.Opacity = 1.0;
            }
        }

        /// <summary>
        /// Occurs when the element is laid out, rendered, and ready for interaction. 
        /// (Inherited from FrameworkElement.)
        /// </summary>
        /// <param name="sender">Owner of the event.</param>
        /// <param name="e">Event data for the event.</param>
        private void OnOutlookBarLoaded(object sender, RoutedEventArgs e)
        {
            ActualItemsCount = Math.Min(MaxVisibleItemsCount, Items.Count);

            Loaded -= OnOutlookBarLoaded;

            storedCount = ActualItemsCount;

            if (swapPanel != null && TryLoadSettings())
            {
                if (IsCompact)
                {
                    if (IsMinimized)
                    {
                        MakeVisibleItems(vVisibleItems);
                    }
                    else
                    {
                        MakeVisibleItems(hVisibleItems);
                    }
                }
                else
                {
                    MakeVisibleItems(ActualItemsCount);
                }
                swapPanel.InvalidateArrange();
            }
            else
            {
                UpdateItems();


                if (IsMinimized)
                {
                    UpdateNavigationItems();
                }
                if (IsCompact)
                {
                    vVisibleItems = ActualItemsCount;

                    if (IsMinimized)
                    {
                        hVisibleItems = 0;
                    }
                    else
                    {
                        hVisibleItems = ActualItemsCount;

                    }
                }
            }
            UpdateSelectedItemContent();

        }

        private bool _isDragging = false;

        DeferredDragAdorner _deferredDragAdorner;
        /// <summary>
        /// Occurs when dragging is started.
        /// </summary>
        /// <param name="sender">Owner of the event.</param>
        /// <param name="e">Event data for the event.</param>
        private void Thumb_DragStarted(object sender, System.Windows.Controls.Primitives.DragStartedEventArgs e)
        {
            e.Handled = true;

            if (!IsCompact && !IsMinimized)
            {
                storeMinWidth = PaneWidth;
            }

            _isDragging = true;

            if (IsDeferredDrag)
            {
                AdornerLayer layer = AdornerLayer.GetAdornerLayer(this);

                if (layer != null)
                {
                    _deferredDragAdorner = new DeferredDragAdorner(this);
                    layer.Add(_deferredDragAdorner);
                }
            }
        }

        /// <summary>
        /// Occurs when dragging is completed.
        /// </summary>
        /// <param name="sender">Owner of the event.</param>
        /// <param name="e">Event data for the event.</param>
        private void Thumb_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
        {
            if (IsCompact)
            {
                ExpandWidth = !IsMinimized ? PaneWidth : storeMinWidth;
            }
            else
            {
                ExpandWidth = IsMinimized ? storeMinWidth : (PaneWidth <= 60 ? 100.0D : PaneWidth);
            }

            e.Handled = true;

            _isDragging = false;

            if (IsDeferredDrag)
            {
                AdornerLayer layer = AdornerLayer.GetAdornerLayer(this);

                if (layer != null && _deferredDragAdorner != null)
                {
                    PaneWidth += _deferredDragAdorner.HorizontalChange;

                    layer.Remove(_deferredDragAdorner);

                    if (IsCompact&&!IsMinimized)
                    {
                        hVisibleItems = GetPendingVisibleItemsCount(PaneWidth);

                        MakeVisibleItems(hVisibleItems);
                    }
                }
            }

        }

        /// <summary>
        /// Invoked directly after defferred drag is completed 
        /// and calculates possible visible items count in accordance of a pending width of Pane.
        /// </summary>
        /// <param name="pendingWidth">Type: System.Double. A new pending width of Pane.</param>
        /// <returns>Type: System.Int32. Possible visible items count.</returns>
        private int GetPendingVisibleItemsCount(double pendingWidth) 
        {
            int index = hVisibleItems;

            if (_menu != null)
            {
                index = 0;

                double menuItemWidth = _menu.Margin.Right + _menu.Margin.Left + _menu.DesiredSize.Width;

                pendingWidth -= menuItemWidth;

                double width = 5;

                while (width < pendingWidth && index < ActualItemsCount)
                {
                    SwapPanelItem model = (from m in swapPanel.ItemsModel where m.Index == index select m).FirstOrDefault();

                    if (model != null && model.UIElement != null)
                    {
                        OutlookBarItem item = model.UIElement as OutlookBarItem;

                        OutlookBarItemModel itemModel = item._itemModel;

                        if (itemModel == null)
                        {
                            SetOutlookBarItemModel(item);
                            itemModel = item._itemModel;
                        }

                        ///item.Visibility = Visibility.Collapsed;
                        width += itemModel.Size.Width + item.Margin.Right + item.Margin.Left;

                        index++;

                    }
                }     
            }

            return index;
        }

        /// <summary>
        /// Occurs directly after Close is called, and can be handled to cancel window closure.
        /// </summary>
        /// <param name="sender">Owner of the event.</param>
        /// <param name="e">Event data for the event.</param>
        private void OnParentWindowClosing(object sender, CancelEventArgs e)
        {
            (sender as Window).Closing -= OnParentWindowClosing;

            if (SavesState)
            {
                TrySaveSettings();
            }
        }
        #endregion

        //------------------------------------------------------
        //
        //  Depenency Property Changed Callbacks
        //
        //------------------------------------------------------
        #region Depenency Property Changed Callbacks

        /// <summary>
        /// Invoked when AnimationsEnabled property changes.
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The DependencyObject on which the property has changed value.</param>
        /// <param name="e">Type: System.Windows.DependencyPropertyChangedEventArgs. Event data that is issued by any event that tracks changes to the effective value of this property.</param>
        private static void OnAnimationsEnabledChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            bool newValue = (bool)e.NewValue;

            OutlookBar bar = sender as OutlookBar;

            if (!newValue)
            {
                ValueSource vSource = DependencyPropertyHelper.GetValueSource(bar, PaneWidthProperty);

                if (vSource.IsAnimated)
                {
                    bar.PaneWidth = bar.PaneWidth;

                    bar.ApplyAnimationClock(OutlookBar.PaneWidthProperty, null);
                }
            }
        }

        /// <summary>
        /// Coerces ItemsGridHeight property.
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The object that the property exists on. When the callback is invoked, the property system will pass this value.</param>
        /// <param name="value">Type: System.Object. The new value of the property, prior to any coercion attempt.</param>
        /// <returns>Type: System.Object. The coerced value (with appropriate type). </returns>
        private static object CoerceItemsGridHeight(DependencyObject sender, object value)
        {
            OutlookBar bar = sender as OutlookBar;

            double pendingValue = (double)value;

            if (pendingValue < 55)
            {
                return 55.0D;
            }

            return value;
        }

        /// <summary>
        /// Invoked whenever ItemsGridHeight property changes. 
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The DependencyObject on which the property has changed value.</param>
        /// <param name="e">Type: System.Windows.DependencyPropertyChangedEventArgs. Event data that is issued by any event that tracks changes to the effective value of this property.</param>
        private static void OnItemsGridHeightChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            OutlookBar bar = sender as OutlookBar;

            double nValue = (double)e.NewValue, oValue = (double)e.OldValue;

            Point p = bar.upDownThumb.TranslatePoint(bar.emptyPoint, bar);

        }

        /// <summary>
        /// Invoked whenever IsMinimized property changes. 
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The DependencyObject on which the property has changed value.</param>
        /// <param name="e">Type: System.Windows.DependencyPropertyChangedEventArgs. Event data that is issued by any event that tracks changes to the effective value of this property.</param>
        private static void OnIsMinimizedPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            bool newValue = (bool)e.NewValue;

            OutlookBar bar = sender as OutlookBar;

            if (newValue)
            {
                if (bar.InnerContentPresenter != null)
                {
                    bar.InnerContentPresenter.ClearValue(ContentPresenter.ContentProperty);
                }
                bar.UpdateNavigationItems();

                if (bar.IsCompact)
                {
                    bar.MakeVisibleItems(bar.vVisibleItems);
                }

                if (!bar.IsLoaded)
                {
                    bar.PaneWidth = 50.0D;
                }
            }
            else
            {
                bar.UpdateSelectedItemContent();

                bar.UpdateNavigationItems(false);

                //
                if (bar.IsCompact)
                {
                    bar.CollapseAllItems();
                }
            }
        }

        /// <summary>
        /// Invoked whenever IsCompact property changes. 
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The DependencyObject on which the property has changed value.</param>
        /// <param name="e">Type: System.Windows.DependencyPropertyChangedEventArgs. Event data that is issued by any event that tracks changes to the effective value of this property.</param>
        private static void OnIsCompactPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            OutlookBar bar = sender as OutlookBar;

            bool newValue = (bool)e.NewValue;

            bar.UpdateItems();

        }

        /// <summary>
        /// Coerces MaxVisibleItemsCount property.
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The object that the property exists on. When the callback is invoked, the property system will pass this value.</param>
        /// <param name="value">Type: System.Object. The new value of the property, prior to any coercion attempt.</param>
        /// <returns>Type: System.Object. The coerced value (with appropriate type). </returns>
        private static object CoerceMaxVisibleItemsCountProperty(DependencyObject sender, object value)
        {
            int newVlaue = (int)value;

            OutlookBar bar = sender as OutlookBar;
            if (newVlaue < 1)
            {
                return 1;
            }
            else if (newVlaue > bar.Items.Count)
            {
                return bar.Items.Count;
            }
            return value;
        }

        /// <summary>
        /// Invoked whenever MaxVisibleItemsCount property changes. 
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The DependencyObject on which the property has changed value.</param>
        /// <param name="e">Type: System.Windows.DependencyPropertyChangedEventArgs. Event data that is issued by any event that tracks changes to the effective value of this property.</param>
        private static void OnMaxVisibleItemsCountPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            OutlookBar bar = sender as OutlookBar;

            bar.ActualItemsCount = Math.Min(bar.MaxVisibleItemsCount, bar.Items.Count);

            bar.UpdateItems();
        }

        /// <summary>
        /// Invoked whenever SelectionMode property changes. 
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The DependencyObject on which the property has changed value.</param>
        /// <param name="e">Type: System.Windows.DependencyPropertyChangedEventArgs. Event data that is issued by any event that tracks changes to the effective value of this property.</param>
        private static void OnSelectionModePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { }

        /// <summary>
        /// Coerces SelectionMode property.
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The object that the property exists on. When the callback is invoked, the property system will pass this value.</param>
        /// <param name="value">Type: System.Object. The new value of the property, prior to any coercion attempt.</param>
        /// <returns>Type: System.Object. The coerced value (with appropriate type). </returns>
        private static object CoerceSelectionModeProperty(DependencyObject sender, object value)
        {
            return SelectionMode.Single;
        }

        /// <summary>
        /// Invoked whenever PaneWidth property changes. 
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The DependencyObject on which the property has changed value.</param>
        /// <param name="e">Type: System.Windows.DependencyPropertyChangedEventArgs. Event data that is issued by any event that tracks changes to the effective value of this property.</param>
        private static void OnPaneWidthPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            OutlookBar bar = sender as OutlookBar;

            ValueSource vSource = DependencyPropertyHelper.GetValueSource(bar, OutlookBar.PaneWidthProperty);

            double newValue = (double)e.NewValue, oldValue = (double)e.OldValue;

            if (bar.IsCompact && bar.IsValidPaneWidth &&
                bar._menu != null && !bar.IsMinimized && !vSource.IsAnimated && bar._isDragging)
            {

                Point p = bar._menu.TranslatePoint(bar.emptyPoint, bar);

                if (newValue > oldValue)
                {
                    if (newValue >= p.X + 70 && bar.hVisibleItems < bar.ActualItemsCount)
                    {
                        SwapPanelItem model = (from m in bar.swapPanel.ItemsModel where m.Index == bar.hVisibleItems select m).FirstOrDefault();

                        if (model != null && model.UIElement != null && model.UIElement.Visibility == Visibility.Collapsed)
                        {
                            model.UIElement.Visibility = Visibility.Visible;

                            bar.hVisibleItems++;
                        }
                    }
                }
                else
                {
                    if (newValue - p.X < 38)
                    {
                        if (bar.hVisibleItems > bar.ActualItemsCount || bar.hVisibleItems <= 0)
                        {
                            bar.hVisibleItems = bar.ActualItemsCount;
                        }

                        SwapPanelItem model = (from m in bar.swapPanel.ItemsModel where m.Index == bar.hVisibleItems - 1 select m).FirstOrDefault();

                        if (model != null && model.UIElement != null)
                        {
                            OutlookBarItem item = model.UIElement as OutlookBarItem;

                            OutlookBarItemModel itemModel = item._itemModel;

                            if (itemModel == null)
                            {
                                bar.SetOutlookBarItemModel(item);
                            }

                            item.Visibility = Visibility.Collapsed;

                            bar.hVisibleItems--;

                            if (bar.hVisibleItems == 2)
                            {
                                bar.storeMinWidth = oldValue + 7;
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Coerces PaneWidth property.
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The object that the property exists on. When the callback is invoked, the property system will pass this value.</param>
        /// <param name="value">Type: System.Object. The new value of the property, prior to any coercion attempt.</param>
        /// <returns>Type: System.Object. The coerced value (with appropriate type). </returns>
        private static object CoercePaneWidthProperty(DependencyObject sender, object value)
        {
            double val = (double)value;

            OutlookBar bar = sender as OutlookBar;

            //Pane's width cannot be less 50.
            if (val <= 55 || (bar.IsDeferredDrag && val <= 65))
            {
                bar.ItemsPaneWidth = 43;

                bar.IsMinimized = true;

                bar.hVisibleItems = 0;

                return 50.0D;
            }
            else if (val >= bar.MaxPaneWidth)
            {
                bar.ItemsPaneWidth = bar.MaxPaneWidth - 7;
                return bar.MaxPaneWidth;
            }

            if (val >= 56 && bar.IsMinimized)
            {
                bar.hVisibleItems = 0;

                if (bar.IsLoaded)
                {
                    bar.IsMinimized = false;
                }
                else
                {
                    bar.ItemsPaneWidth = 43;

                    bar.ExpandWidth = val;
                    bar.storeMinWidth = val;
                    bar.storedCount = bar.ActualItemsCount;
                    return 50.0D;
                }
            }

            bar.ItemsPaneWidth = val - 7;

            return value;
        }

        #endregion

        //------------------------------------------------------
        //
        //  Overrides
        //
        //------------------------------------------------------
        #region Overrides

        /// <summary>
        /// Raises the Initialized event. This method is invoked whenever IsInitialized is set to true.
        /// </summary>
        /// <param name="e">Type: System.EventArgs. Event data for the event.</param>
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);

            if (SavesState)
            {
                Window window = Window.GetWindow(this);

                if (window != null)
                {
                    window.Closing += OnParentWindowClosing;
                }
            }
        }

        /// <summary>
        /// Called when the selection changes.
        /// </summary>
        /// <param name="e">Event data for the event.</param>
        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            base.OnSelectionChanged(e);

            if (AnimatedSelection && pHelper.CanAnimate)
            {
                int previousIndex = -1;

                if (SelectedIndex < 0 ||
                    e.RemovedItems.Count == 0 ||
                    (previousIndex = Items.IndexOf(e.RemovedItems[0])) < 0)
                {
                    return;
                }

                if (TranslateStoryboard == null)
                {
                    BuildTranslateAnimation();
                }

                object ob = e.RemovedItems[0];

                SwapPanelItem prevoiusItem = null, currentItem = null;

                if (ob is DependencyObject)
                {
                    prevoiusItem = (ob as DependencyObject).GetValue(SwapPanel.SwapPanelItemModelProperty) as SwapPanelItem;
                }
                else
                {
                    DependencyObject dob = ItemContainerGenerator.ContainerFromItem(ob);
                    if (ob != null)
                    {
                        prevoiusItem = dob.GetValue(SwapPanel.SwapPanelItemModelProperty) as SwapPanelItem;
                    }
                }

                ob = e.AddedItems[0];

                if (ob is DependencyObject)
                {
                    currentItem = (ob as DependencyObject).GetValue(SwapPanel.SwapPanelItemModelProperty) as SwapPanelItem;
                }
                else
                {
                    DependencyObject dob = ItemContainerGenerator.ContainerFromItem(ob);
                    if (ob != null)
                    {
                        currentItem = dob.GetValue(SwapPanel.SwapPanelItemModelProperty) as SwapPanelItem;
                    }
                }
                if (currentItem == null || prevoiusItem == null)
                {
                    return;
                }

                double X = 0;

                if (IsMinimized)
                {
                    X = prevoiusItem.YLocation > currentItem.YLocation ? -25 : 25;
                }
                else
                {
                    X = prevoiusItem.XLocation < currentItem.XLocation ? -25 : 25;
                }
                SetTransitionAnimationValues(1, 0, 0, X);

                EventHandler handler = null;

                handler = delegate
                {
                    TranslateStoryboard.Completed -= handler;

                    handler = null;

                    SetTransitionAnimationValues(0, 1, X * (-1), 0);

                    UpdateSelectedItemContent();
                    UpdateNavigationItems();

                    TranslateStoryboard.Begin();
                };

                TranslateStoryboard.Completed += handler;

                TranslateStoryboard.Begin();
            }
            else
            {
                UpdateSelectedItemContent();

                UpdateNavigationItems();
            }
        }

        /// <summary>
        ///Creates or identifies the element that is used to display the given item.
        /// </summary>
        /// <returns>Type: System.Windows.DependencyObject. The element that is used to display the given item.</returns>
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new OutlookBarItem() { ParentOutlook = this };
        }
        /// <summary>
        /// Determines if the specified item is (or is eligible to be) its own ItemContainer. 
        /// (Overrides ItemsControl.IsItemItsOwnContainerOverride(Object).)
        /// </summary>
        /// <param name="item">Specified item.</param>
        /// <returns>true if the item is its own ItemContainer; otherwise, false.</returns>
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            bool isValidItem = item is OutlookBarItem;

            if (isValidItem)
            {
                OutlookBarItem baritem = item as OutlookBarItem;

                baritem.ParentOutlook = this;

                DropDownMenuItem menuItem = baritem.GetValue(OutlookBar.DropDownMenuItemProperty) as DropDownMenuItem;

                if (menuItem == null)
                {
                    menuItem = BuildDropDownMenuItem(baritem);

                    baritem.SetValue(OutlookBar.DropDownMenuItemProperty, menuItem);

                    MenuItems.Add(menuItem);
                }

            }
            return isValidItem;
        }


        /// <summary>
        /// Invoked whenever application code or internal processes (such as a rebuilding layout pass) call ApplyTemplate. 
        /// In simplest terms, this means the method is called just before a UI element displays in  app.  
        /// </summary>
        public override void OnApplyTemplate()
        {
            //if not null we must detach event handler for perfomance issue
            if (Thumb != null)
            {
                Thumb.DragDelta -= Thumb_DragDelta;
                Thumb.DragCompleted -= Thumb_DragCompleted;
                Thumb.DragStarted -= Thumb_DragStarted;
            }

            Thumb = GetTemplateChild(OutlookBarThumbName) as Thumb;

            paneGrid = GetTemplateChild(paneGridName) as Grid;

            //before attaching a new handler we must make sure that paneGris is not null 
            //because we are going to resize this control. 
            //This is also enables not to check for null value all the time.
            if (Thumb != null && paneGrid != null)
            {
                Thumb.DragDelta += Thumb_DragDelta;
                Thumb.DragCompleted += Thumb_DragCompleted;
                Thumb.DragStarted += Thumb_DragStarted;
            }

            //if not null we must detach event handler for perfomance issue
            if (upDownThumb != null)
            {
                upDownThumb.DragDelta -= upDownThumb_DragDelta;

            }

            upDownThumb = GetTemplateChild(upDownResizeThumbname) as Thumb;

            itemsGrid = GetTemplateChild(itemsGridname) as Grid;

            if (upDownThumb != null && itemsGrid != null)
            {
                upDownThumb.DragDelta += upDownThumb_DragDelta;
                upDownThumb.DragStarted += upDownThumb_DragStarted;
            }

            ComponentResourceKey rke = new ComponentResourceKey(typeof(OutlookBar), "NavigationSettingsWindowContent");

            NavigationSettingsWindowContent = NavigationHelper.GetThemeResourceByKey<Grid>(rke);
            swapPanel = GetTemplateChild(itemsHostName) as SwapPanel;
            NavigationItemsHost = GetTemplateChild(NavigationItemsHostName) as ListBox;

            InnerContentPresenter = GetTemplateChild(InnerContentPresenterName) as ContentPresenter;

            _menu = GetTemplateChild(MenuName) as DropDownMenu;

            //deferredRect = GetTemplateChild("dragRect") as System.Windows.Shapes.Rectangle;

            //if (deferredRect != null) 
            //{
            //    deferredRect.RenderTransform = new TranslateTransform();
            //}

            base.OnApplyTemplate();
        }

        #endregion

        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
        #region Private Methods

        /// <summary>
        /// Sets OutlookBarItemModel to specified OutlookBarItem.
        /// </summary>
        /// <param name="item">Type: UBCon.Navigation.OutlookBarItem. OutlookBarItem to which model is set.</param>
        private void SetOutlookBarItemModel(OutlookBarItem item) 
        {
            OutlookBarItemModel itemModel = null;
            if (item.Visibility == System.Windows.Visibility.Collapsed)
            {
                item.Visibility = System.Windows.Visibility.Hidden;
                item.Measure(infiniteSize);

                itemModel = new OutlookBarItemModel()
                {
                    Title = item.Title,
                    OutlookBarItem = item,
                    Size = item.DesiredSize
                };

                item.Visibility = System.Windows.Visibility.Collapsed;
            }
            else
            {
                itemModel = new OutlookBarItemModel()
                {
                    Title = item.Title,
                    OutlookBarItem = item,
                    Size = item.DesiredSize
                };
            }

            avItemW = itemModel.Size.Width + avItemW * avCount;
            avItemH = itemModel.Size.Height + avItemH * avCount;

            avCount++;

            avItemW /= avCount;
            avItemH /= avCount;

            item._itemModel = itemModel;
        }

        /// <summary>
        /// Sets Visibility property to Visible specified amount of elements 
        /// </summary>
        /// <param name="count">A number of child elements that must be visible.</param>
        private void MakeVisibleItems(int count)
        {
            if (swapPanel != null)
            {
                for (int i = 0; i < Items.Count; i++)
                {
                    SwapPanelItem itemModel = (from model in swapPanel.ItemsModel where model.Index == i select model).FirstOrDefault();
                    if (itemModel != null)
                    {
                        if (itemModel.UIElement != null)
                        {
                            itemModel.UIElement.Visibility = i < count ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;

                            DropDownMenuItem menuItem = itemModel.UIElement.GetValue(OutlookBar.DropDownMenuItemProperty) as DropDownMenuItem;

                            if (menuItem != null)
                            {
                                menuItem.Visibility = System.Windows.Visibility.Collapsed;

                                if (i >= count)
                                {
                                    menuItem.Visibility = System.Windows.Visibility.Visible;
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Loads OutlookBar's parameters from file.
        /// </summary>
        /// <returns>true if load is successful, false otherwise.</returns>
        private bool TryLoadSettings()
        {
            string fullFileName = (new Uri(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase), defaultFileName))).LocalPath;

            if (!File.Exists(fullFileName))
            {
                return false;
            }

            try
            {
                bool validValue = true;

                BinaryFormatter binary = new BinaryFormatter();

                binary.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full;

                binary.TypeFormat = System.Runtime.Serialization.Formatters.FormatterTypeStyle.TypesAlways;

                binary.FilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;

                FileStream file = new FileStream(fullFileName, FileMode.Open, FileAccess.Read);

                OutlookBarSettings settings = binary.Deserialize(file) as OutlookBarSettings;

                if (settings == null)
                {
                    return false;
                }

                file.Close();

                file.Dispose();

                if (settings.ActualItemsCount != ActualItemsCount || settings.ItemsCount != Items.Count)
                {
                    validValue = false;
                }

                if (validValue)
                {
                    for (int i = 0; i < Items.Count; i++)
                    {
                        DependencyObject dpO = ItemContainerGenerator.ContainerFromItem(Items[i]);

                        SwapPanelItem itemModel = dpO.GetValue(SwapPanel.SwapPanelItemModelProperty) as SwapPanelItem;

                        if (itemModel == null)
                        {
                            validValue = false;

                            break;
                        }

                        itemModel.Index = settings.Indexes[i];
                    }

                    if (validValue)
                    {
                        IsCompact = settings.IsCompact;

                        IsMinimized = settings.IsMinimized;

                        SelectedIndex = settings.SelectedIndex;

                        hVisibleItems = settings.hVisibleItems;

                        vVisibleItems = settings.vVisibleItems;

                        AnimationsEnabled = settings.AnimationsEnabled;

                        ExpandWidth = settings.ExpandWidth;

                        if (!IsMinimized)
                        {
                            PaneWidth = settings.PaneWidth;
                        }
                        else
                        {
                            PaneWidth = 50.0D;
                        }
                    }
                }
                return validValue;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Saves last modified paramateres of the control into a file.
        /// </summary>
        /// <returns>true if saving is successful, false otherwise.</returns>
        private void TrySaveSettings()
        {
            try
            {
                bool canContinue = true;

                OutlookBarSettings settings = new OutlookBarSettings();

                settings.ActualItemsCount = ActualItemsCount;

                settings.hVisibleItems = hVisibleItems;

                settings.IsMinimized = IsMinimized;

                settings.ItemsCount = Items.Count;

                settings.MaxVisibleItemsCount = MaxVisibleItemsCount;

                settings.PaneWidth = PaneWidth;

                settings.vVisibleItems = vVisibleItems;

                settings.IsCompact = IsCompact;

                settings.SelectedIndex = SelectedIndex;

                settings.AnimationsEnabled = AnimationsEnabled;

                settings.ExpandWidth = ExpandWidth;

                for (int i = 0; i < Items.Count; i++)
                {
                    DependencyObject dpO = ItemContainerGenerator.ContainerFromItem(Items[i]);

                    SwapPanelItem itemModel = dpO.GetValue(SwapPanel.SwapPanelItemModelProperty) as SwapPanelItem;

                    if (dpO != null && itemModel != null)
                    {
                        settings.Indexes.Add(itemModel.Index);
                    }
                    else
                    {
                        canContinue = false;
                        break;
                    }
                }
                if (canContinue)
                {
                    BinaryFormatter binary = new BinaryFormatter();

                    binary.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full;

                    binary.TypeFormat = System.Runtime.Serialization.Formatters.FormatterTypeStyle.TypesAlways;

                    binary.FilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;

                    string fullFileName = (new Uri(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase), defaultFileName))).LocalPath;

                    if (File.Exists(fullFileName))
                    {
                        File.Delete(fullFileName);
                    }
                    FileStream file = new FileStream(fullFileName, FileMode.Create, FileAccess.Write);

                    binary.Serialize(file, settings);

                    file.Close();

                    file.Dispose();
                }
            }
            catch (Exception exc)
            {

            }
        }

        /// <summary>
        /// Checks whether specified index is valid.
        /// </summary>
        /// <param name="index">Int value which is validated.</param>
        /// <returns>Valid System.Int32 index</returns>
        private int ValidateIndex(int index)
        {
            if (index < 0)
            {
                index = 0;
            }
            else if (index >= ActualItemsCount)
            {
                index = ActualItemsCount - 1;
            }
            return index;
        }

        /// <summary>
        /// Updates control's content whenever selection changes or reloaded.
        /// </summary>
        private void UpdateSelectedItemContent()
        {
            if (InnerContentPresenter != null && SelectedItem != null)
            {
                if (SelectedItem is OutlookBarItem)
                {
                    OutlookBarItem barItem = SelectedItem as OutlookBarItem;
                    InnerContentPresenter.SetValue(ContentPresenter.ContentProperty, barItem.InnerContent);
                }
            }
        }

        /// <summary>
        /// Shows selected item's NavigationItems when minimized.
        /// </summary>
        /// <param name="shouldAdd">default value is true. If true we add items to list box otherwise clear them.</param>
        private void UpdateNavigationItems(bool shouldAdd = true)
        {
            if (NavigationItemsHost != null)
            {
                NavigationItemsHost.Items.Clear();
                if (shouldAdd)
                {
                    OutlookBarItem barItem = SelectedItem as OutlookBarItem;
                    if (barItem != null)
                    {
                        for (int i = 0; i < barItem.NavigationItems.Count; i++)
                        {
                            barItem.NavigationItems[i].Visibility = System.Windows.Visibility.Visible;
                            NavigationItemsHost.Items.Add(barItem.NavigationItems[i]);
                        }
                        NavigationItemsHost.Items.Add(barItem.AllNavigationItem);
                    }
                }
            }
        }

        /// <summary>
        /// Sets NumericUpDown control.
        /// </summary>
        private void GetNumericUpDown()
        {
            try
            {
                if (NavigationSettingsWindowContent is Grid)
                {
                    Grid grid = NavigationSettingsWindowContent as Grid;
                    if (grid.Children.Count > 0)
                    {
                        StackPanel stackPanel = grid.Children[5] as StackPanel;
                        okButton = stackPanel.Children[1] as ButtonBase;
                        listBox = grid.Children[2] as ListBox;
                        stackPanel = grid.Children[3] as StackPanel;
                        if (stackPanel != null)
                        {
                            upButton = stackPanel.Children[0] as ButtonBase;
                            downButton = stackPanel.Children[1] as ButtonBase;
                        }
                        grid = grid.Children[0] as Grid;
                        if (grid != null && grid.Children.Count > 2)
                        {
                            numericUpDown = grid.Children[1] as NumericUpDown;
                            checkBox = grid.Children[2] as CheckBox;
                        }

                    }
                }
            }
            finally
            {
            }

        }

        /// <summary>
        /// Resets animetion's To and From properties.
        /// </summary>
        /// <param name="opacityFrom">A Double value for From property. The Target is Opacity property.</param>
        /// <param name="opacityTO">A Double value for To property. The Target is Opacity property.</param>
        /// <param name="yFrom">A Double value for From property. The Target is TranslateTransform.Y property.</param>
        /// <param name="yTO">A Double value for From property. The Target is TranslateTransform.X property.</param>
        private void SetTransitionAnimationValues(double opacityFrom, double opacityTO, double yFrom, double yTO)
        {
            opacityDA.From = opacityFrom;
            opacityDA.To = opacityTO;
            translateDA.From = yFrom;
            translateDA.To = yTO;
        }

        /// <summary>
        /// Collapses all items.
        /// </summary>
        private void CollapseAllItems()
        {
            for (int i = 0; i < ActualItemsCount; i++)
            {
                SwapPanelItem model = (from m in swapPanel.ItemsModel where m.Index == i select m).FirstOrDefault();

                if (model != null&&model.UIElement.Visibility == System.Windows.Visibility.Visible) 
                {
                    model.UIElement.Visibility = System.Windows.Visibility.Collapsed;
                }
            }
        }

        /// <summary>
        /// Invoked when menu item is selected.
        /// </summary>
        /// <param name="parameter">Type: Object. Menu items sends selected OutlookBarItem control as paramater.</param>
        private void OnMenuItemSelected(object parameter)
        {
            if (parameter != null && parameter is OutlookBarItem)
            {
                OutlookBarItem barItem = parameter as OutlookBarItem;

                int index = Items.IndexOf(barItem);

                if (index >= 0)
                {
                    SelectedIndex = index;
                }
            }
        }

        /// <summary>
        /// Resets children's Visibility property.
        /// </summary>
        private void UpdateItems()
        {
            for (int i = 0; i < Items.Count; i++)
            {

                UIElement item = ItemContainerGenerator.ContainerFromItem(Items[i]) as UIElement;

                if (item != null)
                {
                    item.Visibility = System.Windows.Visibility.Visible;

                    if (i >= MaxVisibleItemsCount)
                    {
                        item.Visibility = System.Windows.Visibility.Collapsed;
                    }

                    DropDownMenuItem menuItem = item.GetValue(OutlookBar.DropDownMenuItemProperty) as DropDownMenuItem;

                    if (menuItem != null)
                    {
                        menuItem.Visibility = System.Windows.Visibility.Collapsed;

                        if (i >= MaxVisibleItemsCount)
                        {
                            menuItem.Visibility = System.Windows.Visibility.Visible;
                        }
                    }
                }
            }

            SetValue(ItemsGridHeightProperty, double.NaN);

            InvalidateMeasure();

            if (IsCompact)
            {
                hVisibleItems = ActualItemsCount;
                vVisibleItems = ActualItemsCount;
            }
        }

        /// <summary>
        /// Builds storyboards for expand and collapse behaviours. 
        /// </summary>
        /// <param name="isExpand">if true builds expand behaviour animation, false otherwise.</param>
        private void BuildCollapsExpandAnimations(bool isExpand = true)
        {
            Storyboard sb = new Storyboard();
            DoubleAnimation da = new DoubleAnimation()
            {
                EasingFunction = new ExponentialEase()
                {
                    EasingMode = EasingMode.EaseOut,
                    Exponent = 5
                }
            };

            Storyboard.SetTarget(da, this);

            Storyboard.SetTargetProperty(da, new PropertyPath(OutlookBar.PaneWidthProperty));

            da.Duration = new Duration(TimeSpan.FromSeconds(0.40D));

            sb.Children.Add(da);
            if (isExpand)
            {
                da.To = ExpandWidth;

                expandDA = da;

                ExpandStoryboard = sb;

                ExpandStoryboard.Completed += ExpandStoryboard_Completed;
            }
            else
            {
                da.To = 50;

                CollapseStoryboard = sb;

                CollapseStoryboard.Completed += CollapseStoryboard_Completed;
            }
        }

        /// <summary>
        /// Builds menu item for navigation settings.
        /// </summary>
        private void BuildNavigationSettingsMenuItem()
        {
            DropDownMenuItem menuItem = new DropDownMenuItem();

            menuItem.Header = "Navigation settings";

            menuItem.Command = ShowNavigationSettingsCommand;

            MenuItems.Add(menuItem);
        }

        /// <summary>
        /// Creates DropDownMenuItem control for specified OutlookBarItem control.
        /// </summary>
        /// <param name="barItem">Associated OutlookBarItem contol for which menu item is created.</param>
        /// <returns>Type: UBCon.Navigation.DropDownMenuItem. </returns>
        private DropDownMenuItem BuildDropDownMenuItem(OutlookBarItem barItem)
        {
            DropDownMenuItem menuItem = new DropDownMenuItem();

            BindingOperations.SetBinding
                (
                    menuItem,
                    DropDownMenuItem.HeaderProperty,
                    new Binding("Title") { Source = barItem, Mode = BindingMode.OneWay, NotifyOnSourceUpdated = true }
                );

            BindingOperations.SetBinding
                (
                    menuItem,
                    DropDownMenuItem.ImageProperty,
                    new Binding("Image") { Source = barItem, Mode = BindingMode.OneWay, NotifyOnSourceUpdated = true }
                );

            menuItem.Command = MenuItemCommand;

            menuItem.CommandParameter = barItem;

            menuItem.Tag = barItem;

            return menuItem;
        }

        /// <summary>
        /// Invoked when "Up" or "Down" button is pressed.
        /// </summary>
        /// <param name="parameter">Type: System.Object. Sent data by button.</param>
        private void OnUpDownCommandExecuted(object parameter)
        {
            if (parameter == null)
            {
                return;
            }
            if (string.Compare("Up", parameter.ToString()) == 0)
            {
                int index = listBox.SelectedIndex;
                if (index >= 0)
                {
                    int index1 = index - 1;
                    if (index1 < 0)
                    {
                        index1 = models.Count - 1;
                    }
                    models.Move(index, index1);

                }
            }
            else if (string.Compare("Down", parameter.ToString()) == 0)
            {
                int index = listBox.SelectedIndex;
                if (index >= 0)
                {
                    int index1 = index + 1;
                    index1 = index1 % models.Count;
                    models.Move(index, index1);
                    
                }
            }
        }

        #endregion

        //------------------------------------------------------
        //
        //  Fields
        //
        //------------------------------------------------------
        #region Fields
        int _hVisibleItems = 0, _vVisibleItems = 0, ActualItemsCount = 0, avCount = 0, storedCount = 0;
        private ContentPresenter InnerContentPresenter;
        private Thumb Thumb = null, upDownThumb;
        private Grid paneGrid = null, itemsGrid;
        private Storyboard collapseStoryboard, expandStoryboard, translateStoryboard;
        private DoubleAnimation expandDA, translateDA, opacityDA;
        private Command menuItemCommand, showNavigationSettingsCommand, upDownCommand;
        private DropDownMenu _menu;
        private const string MenuName = "PART_Menu",
                             OutlookBarThumbName = "PART_OutlookBarThumb",
                             paneGridName = "paneGrid",
                             upDownResizeThumbname = "OutlookBar_upDownResizeThumb",
                             itemsGridname = "itemsGrid",
                             xPropertyPath = "(FrameworkElement.RenderTransform).(TranslateTransform.X)",
                             itemsHostName = "itemsHost",
                             NavigationItemsHostName = "PART_NavigationItemsHost",
                             InnerContentPresenterName = "PART_InnerContentPresenter",
                             defaultFileName = "OutlookBarSettings.obs";
        private object NavigationSettingsWindowContent;

        private ObservableCollection<OutlookBarItemModel> models = new ObservableCollection<OutlookBarItemModel>();

        private NumericUpDown numericUpDown = null;

        private CheckBox checkBox = null;

        private ButtonBase okButton, upButton, downButton;

        private ListBox listBox, NavigationItemsHost;

        private SwapPanel swapPanel;

        private Point emptyPoint = new Point(0, 0);

        //Infinite size: we use it in order to measure OutlookBarItem control 
        //when its IsMeasureValid property has a false value.
        private Size infiniteSize = new Size(double.PositiveInfinity, double.PositiveInfinity);

        internal ApplicationPerfomanceHelper pHelper = new ApplicationPerfomanceHelper();

        /// <summary>
        /// Store last modified width of outlook bar before minimizing it.
        /// We do this because we need to expand outlook bar to the previuos valid state.
        /// </summary>
        private double ExpandWidth = 0.0D;
        private const double TWOTHIRD = 0.66666;
        private double avItemW = 45.0D, avItemH = 30.0D, storeMinWidth = 0;
        #endregion
    }
}
